package com.mapbased.wfw;

import java.io.*;
import java.net.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.SerializationPolicy;
import com.google.gwt.user.server.rpc.SerializationPolicyLoader;
import com.google.gwt.user.server.rpc.SerializationPolicyProvider;
import com.google.gwt.user.client.rpc.*;
import java.util.zip.GZIPOutputStream;

/**
 *
 * <p>Title: </p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2004</p>
 *
 * <p>Company: mapbased.com</p>
 *
 * @author chy(Baily)
 * @version 1.0
 */
public class AppServlet
    extends HttpServlet
{

    private static final String ACCEPT_ENCODING="Accept-Encoding";
    private static final String CONTENT_ENCODING_GZIP="gzip";
    private static final String CONTENT_ENCODING="Content-Encoding";
    private static final String CHARSET_UTF8="UTF-8";
    private static final String CONTENT_TYPE_TEXT_PLAIN_UTF8=
        "text/plain; charset=utf-8";

    public static final String CONTENT_TYPE="text/html; charset=UTF-8";
    private static final String DOC_TYPE=
        "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n" +
        "  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">";

//    public static final String PARA_ERROR_PAGE="errorPage";
    public static final String PARA_DEVELOPE_MODE="developeMode";


//    public static final String CTX_KEY_PAGE_LOADER="PageLoader";

    //  public static final String CTX_KEY_CONFIG=Constants.CONFIG_KEY;
    public static final String CTX_KEY_GLOBALREF="com.mapbased.wfw.GLOBALREF";

    //Initialize global variables
    private PageLoader loader;
    private String errorPageName="/pages/error.html";
    GlobalRef global;
    //   private Page errorPage;
    public static void loadConfig(GlobalRef g)
    {

    }

    public void init()
        throws ServletException
    {
        this.global=new GlobalRef(this.getServletContext());
        this.loader=global.pageLoader;
     //   global.pageLoader=this.loader;
       // global.servletContext=this.getServletContext();
       // loader.setGlobal(global);



       // this.loader.setDefaulPagePath(pageLocation);

      //  Config c=new DefaultConfig();
       // this.global.setWfwConfig(c);

       // c.postLoad(loader);

       // this.loader.init();
        //this.global.initAppContext();
        //this.getServletContext().setAttribute(CTX_KEY_GLOBALREF,this.global);

        //   this.errorPage=this.loader.getPage(this.errorPageName,null);
    }

    public void doPost(HttpServletRequest request,HttpServletResponse response)
        throws ServletException,IOException
    {
        this.doGet(request,response);


    }

    public void defaultErrorPage(RenderContext ctx)
        throws ServletException,IOException
    {

        HttpServletRequest request=ctx.getRequest();
        HttpServletResponse response=ctx.getResponse();
        PrintWriter out=response.getWriter();
        out.println("<?xml version=\"1.0\"?>");
        out.println(DOC_TYPE);
        out.println(
            "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"zh\" lang=\"zh\">");
        out.println("<head><title>AppServlet Error Page</title></head>");
        out.println("<body bgcolor=\"#ffffff\">");
        String s=String.valueOf(ctx.getRequestMap().get(Constants.
            Error_Page_Msg));
        out.println("<h2 style =\"color:Red;\">");

        out.println("Error Message:");

        out.println(s);
        out.println("</h2><pre>");
        Throwable t=(Throwable)ctx.getRequestMap().get(Constants.
            Error_Page_Throwable);
        if(t != null)
        {
            t.printStackTrace(out);
        }
        out.println("</pre><br/><br/><br/>");
        out.println(
            "<table cellpadding=\"2\" border=\"1\" style=\"width:100%;\" >");
        for(java.util.Iterator i=ctx.getRequestMap().entrySet().iterator();
            i.hasNext();)
        {
            Map.Entry e=(Map.Entry)i.next();
            Object o=e.getKey();
            if(o.equals(Constants.Error_Page_Throwable) ||
               o.equals(Constants.Error_Page_Msg))
            {
                continue;
            }

            out.println("<tr><td>");
            out.println(String.valueOf(o));
            out.println("</td><td>");

            o=e.getValue();
            if(o instanceof Object[])
            {
                Object[] os=(Object[])o;
                for(int mi=0;mi < os.length;mi++)
                {
                    out.println("<ul>");
                    out.println(String.valueOf(os[mi]));

                    out.println("</ul>");

                }
            }
            else
            {
                out.println(String.valueOf(o));
            }

            out.println("</td></tr>");

//        out.println("");



        }
        out.println("</table>");

        out.println("</body>");
        out.println("</html>");
        out.close();
    }

    public void doGet(HttpServletRequest request,HttpServletResponse response)
        throws ServletException,IOException
    {
        request.setCharacterEncoding("utf-8");
        RenderContext ctx=new RenderContext(request,response,this.global);

        ctx.getRequestMap().put(Constants.Page_Start_Time,
                                new Long(System.currentTimeMillis()));

        Page page=this.loader.getPage(ctx);
        if(page == null)
        {
            ctx.getRequestMap().put(Constants.Error_Page_Msg,
                                    "Cannot find page:" +
                                    request.getServletPath());

            this.handleErrorPage(ctx,404);
            return;
        }

        PageHandler ph=page.getPageHandler();
        ctx.setPageHandler(ph);
        try
        {
            String rct=request.getContentType();

            if(rct != null)
            {
                if(rct.startsWith("multipart/form-data;"))
                {
                    MultiPartParser.parseMultiPartRequest(ctx);
                }
                else if(rct.startsWith("text/x-gwt-rpc;"))
                {
                    this.handleRPC(ctx,ph);
                    return;

                }

            }

        }
        catch(IOException ex1)
        {
            ctx.getRequestMap().put(Constants.Error_Page_Throwable,ex1);
            this.handleErrorPage(ctx,500);
            return;

        }
        catch(PageRedirectExceprion re)
        {
            if(re.isServerSide())
            {
                page=this.loader.getPage(re.getPageName(),ctx.getLocale());
                if(page == null)
                {
                    response.sendRedirect(re.getPageName());
                    return;
                }
                ctx.setPageHandler(page.getPageHandler());

            }
            else
            {
                response.sendRedirect(re.getPageName());
                //     response.getOutputStream().close();//??????????
                return;
            }
        }

        while(true)
        {
            try
            {

                Object o=ctx.getPageHandler().dispatchAction(ctx);
                if(o == null)
                {
                    break;
                }
                if(o == com.mapbased.wfw.ExitException.INSTANCE)
                {
                    return;
                }
                String s=o.toString();
                if(s.startsWith("/"))
                {
                    page=this.loader.getPage(s,ctx.getLocale());
                    ctx.setPageHandler(page.getPageHandler());

                }
                else
                {

                    response.sendRedirect(s);
                    return;
                }
            }
            catch(PageRedirectExceprion re)
            {
                if(re.isServerSide())
                {
                    page=this.loader.getPage(re.getPageName(),ctx.getLocale());
                    if(page == null)
                    {
                        response.sendRedirect(re.getPageName());
                        return;
                    }
                    ctx.setPageHandler(page.getPageHandler());

                }
                else
                {
                    response.sendRedirect(re.getPageName());
                    //     response.getOutputStream().close();//??????????
                    return;
                }
            }
            /* catch(IOException ioe)// no out put at all
             {
                 this.getServletContext().log(ioe.getMessage());

             }*/
            catch(ExitException ee)
            {
                //  ee.printStackTrace();
                return;
            }
            catch(Exception ex)
            {
                ctx.getRequestMap().put(Constants.Error_Page_Throwable,ex);
                this.handleErrorPage(ctx,500);
                return;
            }
        }
        response.setContentType(CONTENT_TYPE);
        try
        {
            page.getPageHandler().beforeRender(ctx);
            page.render(ctx);
        }
        catch(PageRedirectExceprion re)
        {
            response.sendRedirect(re.getPageName());

        }
        catch(ExitException ee)
        {}
    }

    /**
     * handle error page,after call this method must return
     * @param ctx RenderContext
     */
    private void handleErrorPage(RenderContext ctx,int statusCode)
        throws IOException,ServletException
    {
        ctx.getRequestMap().remove(Constants.Page_Action);
        Page ep=null;
        try
        {
            ep=this.loader.getPage(this.errorPageName,ctx.getLocale());
        }
        catch(Exception ex)
        {
            ctx.getRequestMap().put(Constants.Error_Page_Throwable,ex);
        }
        if(ep == null)
        {

            this.defaultErrorPage(ctx);
            return;
        }
        ctx.setPageHandler(ep.getPageHandler());
        Object o=ctx.getPageHandler().dispatchAction(ctx);
        ctx.getResponse().setContentType(CONTENT_TYPE);
        ctx.getResponse().setStatus(statusCode);
        ep.render(ctx);
    }

    protected void handleRPC(RenderContext rc,PageHandler ph)
        throws java.io.IOException
    {
        RPCRequest rpcRequest=RPC.decodeRequest(readGwtReq(rc.getRequest()),
                                                null,(GWTHandler)ph);
        String responsePayload=null;
        try
        {
            responsePayload=RPC.invokeAndEncodeResponse(ph,
                rpcRequest.getMethod(),
                rpcRequest.getParameters(),
                rpcRequest.
                getSerializationPolicy());
        }
        catch(Exception ex)
        {
            try
            {
                responsePayload=RPC.encodeResponseForFailure(null,ex);
            }
            catch(SerializationException ex1)
            {
                responsePayload=ex1.toString();
            }
        }

        byte[] reply=responsePayload.getBytes(CHARSET_UTF8);
        String contentType=CONTENT_TYPE_TEXT_PLAIN_UTF8;
        HttpServletRequest request=rc.getRequest();
        HttpServletResponse response=rc.getResponse();

        if(acceptsGzipEncoding(request)
           && responsePayload.length() > 256)
        {
            // Compress the reply and adjust headers.
            //
            ByteArrayOutputStream output=null;
            GZIPOutputStream gzipOutputStream=null;
            Throwable caught=null;
            try
            {
                output=new ByteArrayOutputStream(reply.length);
                gzipOutputStream=new GZIPOutputStream(output);
                gzipOutputStream.write(reply);
                gzipOutputStream.finish();
                gzipOutputStream.flush();
                response.setHeader(CONTENT_ENCODING,CONTENT_ENCODING_GZIP);
                reply=output.toByteArray();
            }
            catch(IOException e)
            {
                caught=e;
            }
            finally
            {
                if(null != gzipOutputStream)
                {
                    gzipOutputStream.close();
                }
                if(null != output)
                {
                    output.close();
                }
            }

            if(caught != null)
            {
                getServletContext().log("Unable to compress response",caught);
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                return;
            }
        }

        // Send the reply.
        //
        response.setContentLength(reply.length);
        response.setContentType(contentType);
        response.setStatus(HttpServletResponse.SC_OK);
        response.getOutputStream().write(reply);

    }

    /**
     * Return true if the response object accepts Gzip encoding. This is done by
     * checking that the accept-encoding header specifies gzip as a supported
     * encoding.
     */
    private static boolean acceptsGzipEncoding(HttpServletRequest request)
    {
        assert(request != null);

        String acceptEncoding=request.getHeader(ACCEPT_ENCODING);
        if(null == acceptEncoding)
        {
            return false;
        }

        return(acceptEncoding.indexOf(CONTENT_ENCODING_GZIP) != -1);
    }

    private static String readGwtReq(HttpServletRequest request)
        throws IOException
    {
        int contentLength=request.getContentLength();
        InputStream in=request.getInputStream();
        try
        {
            byte[] payload=new byte[contentLength];
            int offset=0;
            int len=contentLength;
            int byteCount;
            while(offset < contentLength)
            {
                byteCount=in.read(payload,offset,len);
                if(byteCount == -1)
                {
                    throw new IOException("Client did not send " +
                                          contentLength
                                          + " bytes as expected");
                }
                offset+=byteCount;
                len-=byteCount;
            }
            return new String(payload,"UTF-8");
        }
        finally
        {
            if(in != null)
            {
                in.close();
            }
        }

    }

    /**
     * Called by the servlet container to indicate to a servlet that the
     * servlet is being taken out of service.
     *
     * @todo Implement this javax.servlet.Servlet method
     */
    public void destroy()
    {
        super.destroy();
        this.global.destory();
        this.global=null;
    }
}
